﻿using System;
using System.Collections.Generic;
using FinanSoft.Utilities;
using FinanSoft.Entities;
using System.Data.SqlClient;
using System.Xml;

namespace FinanSoft.DataLayer
{
    class MoneyDataManager
    {
        #region Singleton

        private MoneyDataManager()
        { }

        /// <summary>
        /// Única instancia de esta clase
        /// </summary>
        private static MoneyDataManager _instance;

        /// <summary>
        /// Método para implementar el patrón de Singleton
        /// </summary>
        /// <returns>Instancia única de la clase</returns>
        public static MoneyDataManager GetInstance()
        {
            if (_instance == null)
                _instance = new MoneyDataManager();
            return _instance;
        }

        #endregion

        #region Public Methods

        /// <summary>
        /// Obtiene la información de las monedas existentes en el sistema
        /// </summary>
        /// <returns>Un Documento XML con las monedas estructuradas según su tipo(loca, del sistema, agregada)</returns>
        public List<Currency> GetCurrencyInformation()
        {
            try
            {
                AppConfigReader appConfig = AppConfigReader.GetInstance();
                string spName = Constants.SP_NAME_GET_CURRENCY_INFORMATION;
                SqlParameter[] parameters = new SqlParameter[0];
                SqlDataHelper dataHelper = new SqlDataHelper(appConfig.GetConnectionString(Constants.DB_CONN));
                SqlDataReader result = dataHelper.ExecuteStoredProcedure(spName, parameters);
                if (result.HasRows)
                {
                    result.Read();
                    string currencyXml = result.GetString(0);
                    dataHelper.CloseReader();
                    currencyXml = Constants.ROOT_NODE_BEGINNING + currencyXml + Constants.ROOT_NODE_ENDING;
                    XmlDocument xDoc = new XmlDocument();
                    xDoc.LoadXml(currencyXml);
                    return buildCurrencyList(xDoc);
                }
                else
                    return new List<Currency>();
            }
            catch (Exception ex)
            {
                return null;
            }
        }

        /// <summary>
        /// Agrega una moneda a la base de datos
        /// </summary>
        /// <param name="newCurrency">Moneda con el nuevo símbolo, abreviación y nombre</param>
        /// <returns>Devuelve la información de la moneda agregada, incluyendo el Id</returns>
        public Currency AddCurrency(Currency newCurrency)
        {
            try
            {
                AppConfigReader appConfig = AppConfigReader.GetInstance();
                string spName = Constants.SP_NAME_ADD_CURRENCY;
                SqlParameter[] parameters = new SqlParameter[3];
                parameters[0] = new SqlParameter(Constants.SP_PARAM_ADD_CURRENCY_NAME, newCurrency.Name);
                parameters[1] = new SqlParameter(Constants.SP_PARAM_ADD_CURRENCY_SYMBOL, newCurrency.Symbol);
                parameters[2] = new SqlParameter(Constants.SP_PARAM_ADD_CURRENCY_ABBREVIATION, newCurrency.Abbreviation);
                SqlDataHelper dataHelper = new SqlDataHelper(appConfig.GetConnectionString(Constants.DB_CONN));
                SqlDataReader result = dataHelper.ExecuteStoredProcedure(spName, parameters);
                if (result.HasRows)
                {
                    result.Read();
                    String symbol = (string) result[Constants.SP_COLUMN_ADD_CURRENCY_SYMBOL];
                    String abbreviation = (string)result[Constants.SP_COLUMN_ADD_CURRENCY_ABBREVIATION];
                    String name = (string)result[Constants.SP_COLUMN_ADD_CURRENCY_NAME];
                    int id = Convert.ToInt32(result[Constants.SP_COLUMN_ADD_CURRENCY_ID]);
                    dataHelper.CloseReader();
                    return new Currency(symbol, abbreviation, id, 0, Constants.CurrencyType.added, name);
                }
                    return null;
            }
            catch (Exception ex)
            {
                return null;
            }
        }

        /// <summary>
        /// Intercambia los campos viejos de una moneda por los que recibe el método. No afecta el Id ni el tipo de
        /// cambio de la moneda.
        /// </summary>
        /// <param name="newCurrency">Nueva moneda por la que se intercambia la anterior</param>
        /// <returns>Devuelve si se editó la moneda exitósamente</returns>
        public bool EditCurrency(Currency newCurrency)
        {
            try
            {
                AppConfigReader appConfig = AppConfigReader.GetInstance();
                string spName = Constants.SP_NAME_EDIT_CURRENCY;
                SqlParameter[] parameters = new SqlParameter[4];
                parameters[0] = new SqlParameter(Constants.SP_PARAM_EDIT_CURRENCY_NAME, newCurrency.Name);
                parameters[1] = new SqlParameter(Constants.SP_PARAM_EDIT_CURRENCY_SYMBOL, newCurrency.Symbol);
                parameters[2] = new SqlParameter(Constants.SP_PARAM_EDIT_CURRENCY_ABBREVIATION, newCurrency.Abbreviation);
                parameters[3] = new SqlParameter(Constants.SP_PARAM_EDIT_CURRENCY_ID, newCurrency.Id);
                SqlDataHelper dataHelper = new SqlDataHelper(appConfig.GetConnectionString(Constants.DB_CONN));
                SqlDataReader result = dataHelper.ExecuteStoredProcedure(spName, parameters);
                dataHelper.CloseReader();
                return true;
            }
            catch (Exception ex)
            {
                return false;
            }
        }

        /// <summary>
        /// Elimina una moneda con el id dado de la base de datos. No la logra eliminar si la moneda tiene asociada alguna
        /// cuenta o si tiene un historial en el tipo de cambio.
        /// </summary>
        /// <param name="currencyId">Id de la moneda que se quiere eliminar</param>
        /// <returns>Devuelve si se pudo eliminar exitósamente</returns>
        public bool DeleteCurrency(int currencyId)
        {
            try
            {
                AppConfigReader appConfig = AppConfigReader.GetInstance();
                string spName = Constants.SP_NAME_DELETE_CURRENCY;
                SqlParameter[] parameters = new SqlParameter[1];
                parameters[0] = new SqlParameter(Constants.SP_PARAM_DELETE_CURRENCY_ID, currencyId);
                SqlDataHelper dataHelper = new SqlDataHelper(appConfig.GetConnectionString(Constants.DB_CONN));
                SqlDataReader result = dataHelper.ExecuteStoredProcedure(spName, parameters);
                dataHelper.CloseReader();
                return true;
            }
            catch (Exception ex)
            {
                return false;
            }
        }

        public bool SetCurrencyExchange(int currencyId, DateTime initialDate, DateTime finalDate, double localAmount, int userId)
        {
            try
            {
                AppConfigReader appConfig = AppConfigReader.GetInstance();
                string spName = Constants.SP_NAME_SET_EXCHANGE_RATE;
                SqlParameter[] parameters = new SqlParameter[5];
                parameters[0] = new SqlParameter(Constants.SP_PARAM_SET_EXCHANGE_RATE_CURRENCY_ID, currencyId);
                parameters[1] = new SqlParameter(Constants.SP_PARAM_SET_EXCHANGE_RATE_INITIAL_DATE, initialDate);
                parameters[2] = new SqlParameter(Constants.SP_PARAM_SET_EXCHANGE_RATE_FINAL_DATE, finalDate);
                parameters[3] = new SqlParameter(Constants.SP_PARAM_SET_EXCHANGE_RATE_LOCAL_AMOUNT, localAmount);
                parameters[4] = new SqlParameter(Constants.SP_PARAM_SET_EXCHANGE_RATE_USER_ID, userId);
                SqlDataHelper dataHelper = new SqlDataHelper(appConfig.GetConnectionString(Constants.DB_CONN));
                SqlDataReader result = dataHelper.ExecuteStoredProcedure(spName, parameters);
                dataHelper.CloseReader();
                return true;
            }
            catch (Exception ex)
            {
                return false;
            }
        }
        #endregion

        #region Protected/Private methods

        /// <summary>
        /// Utiliza el documento XML para crear objetos de tipo Currency y establecerles
        /// su respectivo tipo.
        /// </summary>
        /// <param name="xDoc">Documento XML donde se encuentran la información de monedas</param>
        /// <returns>Lista de monedas con su tipo establecido</returns>
        private List<Currency> buildCurrencyList(XmlDocument xDoc)
        {
            AppConfigReader appConfig = AppConfigReader.GetInstance();
            XmlNodeList typeList = xDoc.FirstChild.ChildNodes;
            List<Currency> availableCurrencies = new List<Currency>();
            foreach (XmlNode typeNode in typeList)
            {
                foreach(XmlNode currencyNode in typeNode)
                {
                    String symbol = currencyNode.Attributes.GetNamedItem(Constants.TAG_NAME_CURRENCY_SYMBOL).Value;
                    String abbreviation = currencyNode.Attributes.GetNamedItem(Constants.TAG_NAME_CURRENCY_ABBREVIATION).Value;
                    String name = currencyNode.Attributes.GetNamedItem(Constants.TAG_NAME_CURRENCY_NAME).Value;
                    int id = Convert.ToInt32(currencyNode.Attributes.GetNamedItem(Constants.TAG_NAME_CURRENCY_ID).Value);

                    XmlNode toLocalAmountNode = currencyNode.Attributes.GetNamedItem(Constants.TAG_NAME_CURRENCY_TO_LOCAL_AMOUNT);
                    double toLocalAmount = 1;
                    if (toLocalAmountNode != null)
                        toLocalAmount = Convert.ToDouble(toLocalAmountNode.Value.Replace('.', ','));
                    Constants.CurrencyType type = getCurrencyType(typeNode);
                    availableCurrencies.Add(new Currency(symbol, abbreviation, id, toLocalAmount, type, name));
                }
            }
            return availableCurrencies;
        }

        /// <summary>
        /// Devuelve el tipo de moneda asociada a un nodo XML (el que engloba monedas)
        /// </summary>
        /// <param name="typeNode">Nodo xml donde se engloban monedas</param>
        /// <returns>Tipo de las monedas contenidas en el nodo</returns>
        private Constants.CurrencyType getCurrencyType(XmlNode typeNode)
        {
            String typeName = typeNode.Attributes.GetNamedItem(Constants.TAG_NAME_CURRENCY_TYPE_NAME).Value;
            if (typeName.Equals(Constants.ADDED_TYPE))
                return Constants.CurrencyType.added;
            else if (typeName.Equals(Constants.LOCAL_TYPE))
                return Constants.CurrencyType.local;
            else if (typeName.Equals(Constants.SYSTEM_TYPE))
                return Constants.CurrencyType.system;
            else
                return Constants.CurrencyType.invalid;
        }

        /*
        /// <summary>
        /// Dice si el Stored Procedure fue efectuado correctamente o no
        /// </summary>
        /// <param name="result">ResultSet que indica si fue efectuado correctamente o no</param>
        /// <param name="helper">DataHelper con que la operación fue efectuada</param>
        /// <returns>Verdadero si el SP se ejecutó bien, de lo contrario falso</returns>
        private bool getResult(SqlDataReader result, SqlDataHelper helper)
        {
            result.Read();
            bool actionPerformed = Convert.ToInt32(result[AppConfigReader.GetInstance().GetProperty(Constants.SP_OUTPUT_ACTION_PERFORMED_RESULT)]) == 0 ? false : true;
            helper.CloseConnection();
            return actionPerformed;
        }*/

        #endregion
    }
}
